﻿using SIR.Common;
using SIR.Common.Condition;
using SIR.DAL;
using SIR.Entities.Interfaces;
using System;
using System.Drawing;
using System.Linq;

namespace SIR.Entities.Implementations
{
	/// <summary>
	/// Represents an image within the system
	/// </summary>
	public class Image : Entity, IImage
	{
		/// <summary>
		/// Gets or sets the name of the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public string Name { get; set; }
		/// <summary>
		/// Gets or sets the file type for the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public string FileType { get; set; }
		/// <summary>
		/// Gets or sets the size for the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public long Size { get; set; }
		/// <summary>
		/// Gets or sets the folder identifier for the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public long FolderId { get; set; }
		/// <summary>
		/// Gets or sets the user identifier for the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public long UserId { get; set; }
		/// <summary>
		/// Gets or sets whether or not the image is private
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public bool IsPrivate { get; set; }
		/// <summary>
		/// Gets or sets whether or not the containing folder is private
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.Read)]
		public bool IsFolderPrivate { get; set; }
		/// <summary>
		/// Gets or sets the date and time that the image was uploaded
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.Read)]
		public DateTime Uploaded { get; set; }

		private IImageAnalysis _analysis;
		/// <summary>
		/// Gets the image analysis data for the image
		/// </summary>
		[Newtonsoft.Json.JsonIgnore]
		[DatabaseAccess(DatabaseAccessRights.None)]
		public IImageAnalysis Analysis
		{
			get
			{
				if (_analysis == null)
					_analysis = DataServer.Find<ImageAnalysis>(new Predicate("Id", Id, PredicateOperator.EqualTo)).Results.FirstOrDefault();
				return _analysis;
			}
		}

		private IImageData _data;
		/// <summary>
		/// Gets the image data for the image
		/// </summary>
		[Newtonsoft.Json.JsonIgnore]
		[DatabaseAccess(DatabaseAccessRights.None)]
		public IImageData Data
		{
			get
			{
				if (_data == null)
					_data = DataServer.Find<ImageData>(new Predicate("ImageId", Id, PredicateOperator.EqualTo)).Results.FirstOrDefault();
				return _data;
			}
		}

		private IImageThumb _thumb;
		/// <summary>
		/// Gets the thumb for the image
		/// </summary>
		[Newtonsoft.Json.JsonIgnore]
		[DatabaseAccess(DatabaseAccessRights.None)]
		public IImageThumb Thumb
		{
			get
			{
				if (_thumb == null)
					_thumb = DataServer.Find<ImageThumb>(new Predicate("ImageId", Id, PredicateOperator.EqualTo)).Results.FirstOrDefault();
				return _thumb;
			}
		}
		/// <summary>
		/// Gets the processing status for the image
		/// </summary>
		[DatabaseAccess(DatabaseAccessRights.None)]
		public ProcessingStatus Status { get; set; }
		/// <summary>
		/// Gets or sets the identifier associated to the processing status
		/// </summary>
		[Newtonsoft.Json.JsonIgnore]
		[DatabaseAccess(DatabaseAccessRights.ReadWrite)]
		public int StatusId
		{
			get { return (int)Status; }
			set { Status = (ProcessingStatus)value; }
		}

		/// <summary>
		/// Saves an image with all of the correct properties to the database
		/// </summary>
		/// <param name="userId">The identifier for the user</param>
		/// <param name="folderId">The containing folder of the image</param>
		/// <param name="fileName">the name of the file used to upload the image</param>
		/// <param name="fileType">The file type of the image</param>
		/// <param name="length">The length or size of the image</param>
		/// <param name="bytes">The byte array for the image</param>
		/// <param name="isPrivate">Whether or not the image is private</param>
		/// <param name="processingPriority">The processing priority</param>
		/// <param name="image">The image object that was inserted</param>
		/// <param name="errorMessage">Any error message received during the process</param>
		/// <returns>True if the save was successful</returns>
		public static bool SaveImage(long? userId, long folderId, string fileName, string fileType,
			long length, byte[] bytes, bool isPrivate,
			ProcessingPriority processingPriority, out Image image, out string errorMessage)
		{
			// Create the new image object
			image = new Image()
			{
				UserId = userId.HasValue ? userId.Value : -1,
				Name = fileName,
				Size = length,
				FileType = fileType,
				FolderId = folderId,
				IsPrivate = isPrivate
			};

			// Save the image to the database
			var saveResult = DAL.DataServer.Save<Image>(image);

			// Save Image Data
			if (saveResult.WasSuccessful)
			{
				// Save the byte array for the image
				image = saveResult.NewValue;
				var imageData = new ImageData()
				{
					ImageId = saveResult.NewValue.Id,
					Data = bytes
				};

				var saveDataResult = DAL.DataServer.Save<ImageData>(imageData);

				// Create a new bitmap and reduce it down to fit within a 170x170 square
				var bitmap = new System.Drawing.Bitmap(new System.IO.MemoryStream(bytes));
				// Get the formate
				var format = bitmap.RawFormat;
				// Calculate the ratio
				double ratio = Convert.ToDouble(bitmap.Width) / bitmap.Height;
				// Determine the width and height based on the ratio, max of 170
				int width = ratio > 1 ? 170 : Convert.ToInt32(Math.Floor(Math.Round(170.0 / ratio, 0))),
					height = ratio < 1 ? 170 : Convert.ToInt32(Math.Floor(Math.Round(170.0 / ratio, 0)));
				// Create the thumb bitmap with the new sizes
				var thumbBitmap = new System.Drawing.Bitmap(width, height);
				var graphics = Graphics.FromImage(thumbBitmap);
				graphics.DrawImage(bitmap, 0, 0, width, height);
				var thumbStream = new System.IO.MemoryStream();
				// Save the binary data to the bitmap
				thumbBitmap.Save(thumbStream, format);
				// Get the new binary array
				var data = thumbStream.ToArray();

				// Create and save the thumbnail
				var imageThumb = new ImageThumb()
				{
					ImageId = image.Id,
					Data = data
				};

				DAL.DataServer.Save<ImageThumb>(imageThumb);

				// Add the image to the queue
				var queueResult = DAL.DataServer.Save(new QueuedImage()
				{
					ImageId = image.Id,
					Priority = processingPriority
				});

				// Set the error message and the result the whether or not the image could be added to the queue
				errorMessage = queueResult.ErrorMessage;
				return queueResult.WasSuccessful;
			}

			// An error occured so fail the result
			errorMessage = saveResult.ErrorMessage;
			image = null;
			return false;
		}
	}
}

